/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { HttpUrl } from '../HttpUrl';
import { OkHttp } from '../OkHttp';
import { Response } from '../Response';
import { Headers } from '../Headers'
import { socket } from '@kit.NetworkKit';
import { BufferedSource } from '../okio/BufferedSource';
import { ResponseBody } from '../ResponseBody';
import { TimeUnit } from './TimeUnit';
import { ArrayIndexOutOfBoundsException, IllegalArgumentException, IllegalStateException } from '../Error/error';
import { RequestBody } from '../RequestBody';

export const userAgent = `okhttp/${OkHttp.VERSION}`
export const EMPTY_HEADERS = Headers.headersOf()
export const  EMPTY_BYTE_ARRAY = new Uint8Array(0);
export const EMPTY_RESPONSE = ResponseBody.bytesToResponseBody(EMPTY_BYTE_ARRAY)
export const EMPTY_REQUEST = RequestBody.BytesToRequestBody(EMPTY_BYTE_ARRAY)

export function toHostHeader(url: HttpUrl, includeDefaultPort: boolean = false): string {
    let host = ''
    if(url.host.includes(':')){
        host = `[${url.host}]`
    }else{
        host = url.host
    }
    if (includeDefaultPort || HttpUrl.defaultPort(url.scheme) !== url.port){
        return `${host}:${url.port}`
    }
    return host
}


/** Returns true if an HTTP request for {@code a} and {@code b} can reuse a connection. */
export function canReuseConnectionFor(a: HttpUrl, b: HttpUrl): boolean {
    return a.host === b.host
        && a.port === b.port
        && a.scheme === b.scheme;
}

export function isHealthy(socket: socket.TCPSocket | socket.TLSSocket  ,source: BufferedSource): boolean {
    return true
    // try{
    //     let readTimeout = socket.soTimeout
    //     try{
    //         socket.soTimeout = 1
    //         !source.exhausted()
    //     }finally {
    //         socket.soTimeout = readTimeout
    //     }
    // }catch (_) {
    //     if (_) {
    //         return true
    //     }else {
    //         return false
    //     }
    // }
}
/** Returns the Content-Length as reported by the response headers. */
export function headersContentLength(response: Response): number {
    let contentLength = response.headers.get("Content-Length")
    if (contentLength === null || contentLength === undefined){
        return -1
    }else {
        return toLongOrDefault(contentLength, -1)
    }
}

export function toLongOrDefault(str: string, defaultValue: number): number {
    try {
        return parseInt(str, 10)
    } catch (_) {
         return defaultValue
    }
}

export function checkOffsetAndCount(arrayLength: number, offset: number, count: number) {
    if (offset < 0 || count < 0 || offset > arrayLength || arrayLength - offset < count) {
        throw new ArrayIndexOutOfBoundsException('')
    }
}

export function kotlinRemovePrefix(str: string, prefix: string): string {
    return str.startsWith(prefix) ? str.slice(prefix.length) : str;
}

export function kotlinCheck(condition: boolean, lazyMessage: string): asserts condition {
    if (!condition) {
        throw new IllegalStateException(lazyMessage);
    }
}

export function kotlinCheckNotNull(condition: any, lazyMessage: string): asserts condition {
    if (condition === null) {
        throw new IllegalStateException(lazyMessage);
    }
}

export function kotlinRequire(condition: boolean, lazyMessage: string): asserts condition {
    if (!condition) {
        throw new IllegalArgumentException(lazyMessage);
    }
}

export function checkDuration(name: string, duration: number, unit?: TimeUnit): number {
    kotlinCheck(duration >= 0, name + " < 0")
    kotlinCheck(unit !== null && unit !== undefined, "unit == null")
    let millis:number = unit!.toMillis(duration)
    kotlinRequire(millis <= Number.MAX_VALUE, name + " too large.")
    kotlinRequire(millis != 0 || duration <= 0, name + " too small.")
    return millis
}


export class Utils {
    static VERIFY_AS_IP_ADDRESS: string = "([0-9a-fA-F]*:[0-9a-fA-F:.]*)|([\\d.]+)";

    public static getDomainOrIp(url: string): string {
        let result = "";
        let j = 0, startIndex = 0, endIndex = 0;
        for (let i = 0; i < url.length; i++) {
            if (url.charAt(i) == '/') {
                j++;
                if (j == 2)
                    startIndex = i;
                else if (j == 3)
                    endIndex = i;
            }

        }
        result = url.substring(startIndex + 1, endIndex == 0 ? url.length : endIndex);
        return result;
    }

    /**
     * Increments {@code pos} until {@code input[pos]} is not ASCII whitespace. Stops at {@code
     * limit}.
     */
    static indexOfFirstNonAsciiWhitespace(input: string, startIndex: number, endIndex: number): number {
        for (var i = startIndex; i < endIndex; i++) {
            switch (input.charAt(i)) {
                case '\t':
                case '\n':
                case '\u000C':
                case '\r':
                case ' ':
                    continue;
                default:
                    return i;
            }
        }
        return endIndex;
    }

    /**
     * Decrements {@code limit} until {@code input[limit - 1]} is not ASCII whitespace. Stops at
     * {@code pos}.
     */
    static indexOfLastNonAsciiWhitespace(input: string, startIndex: number, endIndex: number): number {
        for (var i = endIndex - 1; i >= startIndex; i--) {
            switch (input.charAt(i)) {
                case '\t':
                case '\n':
                case '\u000C':
                case '\r':
                case ' ':
                    continue;
                default:
                    return i + 1;
            }
        }
        return startIndex;
    }

    /**
     * Returns the index of the first character in {@code input} that contains a character in {@code
     * delimiters}. Returns limit if there is no such character.
     */
    static delimiterOffset(input: string, pos: number, limit: number, delimiters: string): number {
        for (var i = pos; i < limit; i++) {
            if (delimiters.indexOf(input.charAt(i)) != -1) return i;
        }
        return limit;
    }

    /**
     * 将ArrayBuffer 转换成string
     * @param buf ArrayBuffer
     */
    public static Utf8ArrayToStr(buf: ArrayBuffer) {
        let arr = new Uint8Array(buf);
        let str = '';
        for (let i = 0; i < arr.length; i++) {
            str += String.fromCharCode(arr[i]);
        }
        return str
    }

    //字符串转ArrayBuff
    public static strToArrayBuffer(str) {
        var bytes = new Array();
        var len, c;
        len = str.length;
        for (var i = 0; i < len; i++) {
            c = str.charCodeAt(i);
            if (c >= 0x010000 && c <= 0x10FFFF) {
                bytes.push(((c >> 18) & 0x07) | 0xF0);
                bytes.push(((c >> 12) & 0x3F) | 0x80);
                bytes.push(((c >> 6) & 0x3F) | 0x80);
                bytes.push((c & 0x3F) | 0x80);
            } else if (c >= 0x000800 && c <= 0x00FFFF) {
                bytes.push(((c >> 12) & 0x0F) | 0xE0);
                bytes.push(((c >> 6) & 0x3F) | 0x80);
                bytes.push((c & 0x3F) | 0x80);
            } else if (c >= 0x000080 && c <= 0x0007FF) {
                bytes.push(((c >> 6) & 0x1F) | 0xC0);
                bytes.push((c & 0x3F) | 0x80);
            } else {
                bytes.push(c & 0xFF);
            }
        }
        var array = new Int8Array(bytes.length);
        for (var i = 0; i <= bytes.length; i++) {
            array[i] = bytes[i];
        }
        return array.buffer;
    }
}

